package io.xccit.train.common.aspect;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.support.spring.PropertyPreFilters;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;
import java.util.UUID;


/**
 * @author CH_ywx
 * @version 1.0
 * @create 2024/11/3
 * <p>Log切面</p>
 */
@Aspect
@Component
public class LogAspect {
    public LogAspect(){}
    private static final Logger LOG = LoggerFactory.getLogger(LogAspect.class.getName());

    /**
     * 标识切点
     * public * io.xccit..*Controller.*(..) 所有io.xccit包下的后缀为Controller类中的所有方法
     */
    @Pointcut("execution(public * io.xccit.train..*Controller.*(..))")
    public void controllerPointcuts(){}

    /**
     * 前置通知
     * @param joinPoint 切点
     * 记录请求信息 排除请求参数中的特殊类型参数
     */
    @Before("controllerPointcuts()")
    public void beFore(JoinPoint joinPoint){
        /*日志唯一流水号 配置在logback-spring.xml中*/
        MDC.put("LOG_ID",System.currentTimeMillis()+ UUID.randomUUID().toString().replace("-",""));
        /*请求参数*/
        Object[] args = joinPoint.getArgs();
        /*排除特殊类型参数*/
        Object[] arguments = new Object[args.length];
        for (int i = 0; i < args.length; i++){
            if (args[i] instanceof ServletRequest
                    || args[i] instanceof ServletResponse
                    || args[i] instanceof MultipartFile){
                continue;
            }
            arguments[i] = args[i];
        }
        /*排除敏感数据*/
        String[] excludeProperties = {"idCard","phone","password","newPassword"};
        PropertyPreFilters filters = new PropertyPreFilters();
        PropertyPreFilters.MySimplePropertyPreFilter filter = filters.addFilter();
        filter.addExcludes(excludeProperties);
        ServletRequestAttributes attributes = (ServletRequestAttributes)RequestContextHolder.getRequestAttributes();
        LOG.info("请求地址：{} {}",attributes.getRequest().getRequestURI(),attributes.getRequest().getMethod());
        LOG.info("请求类名：{},请求方法:{}",joinPoint.getSignature().getDeclaringTypeName(),joinPoint.getSignature().getName());
        LOG.info("请求参数：{}", JSON.toJSONString(arguments,filter));
        LOG.info("远程地址：{}",attributes.getRequest().getRemoteAddr());
    }

    /**
     * 环绕通知
     * @param proceedingJoinPoint 切点
     * @return 方法的返回值
     * @throws Throwable
     * 记录方法返回值 执行时间
     */
    @Around("controllerPointcuts()")
    public Object doAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        long startTime = System.currentTimeMillis();
        Object result = proceedingJoinPoint.proceed();
        /*排除敏感字段*/
        String[] excludeProperties = {"idCard","phone","password","newPassword"};
        PropertyPreFilters filters = new PropertyPreFilters();
        PropertyPreFilters.MySimplePropertyPreFilter filter = filters.addFilter();
        filter.addExcludes(excludeProperties);
        LOG.info("返回结果：{}",JSON.toJSONString(result,filter));
        LOG.info("-------结束 耗时：{}ms-------",System.currentTimeMillis() - startTime);
        return result;
    }
}
